home *** CD-ROM | disk | FTP | other *** search
- Fortify - fortified memory allocation shell for C and C++
- ---------------------------------------------------------
- written by Simon P. Bullen
- Cybergraphic
-
- Release 1.0, 2/2/1995
-
-
-
-
- This software is not public domain. All material in
- this archive is © Copyright 1995 Simon P. Bullen. The
- software is freely distrubtable, with the condition that no
- more than a nominal fee is charged for media. Everything in
- this distrubution must be kept together, in original,
- unmodified form.
- The files may be modified for your own personal use, but
- modified files may not be distributed.
- The material is provided "as is" without warranty of any
- kind. The author accepts no responsibilty for damage caused
- by this software.
-
-
-
- email: sbullen@ozemail.com.au
- snail: Simon P. Bullen
- PO BOX 12138
- A'Beckett St
- Melbourne 3000
- Australia
-
-
-
- CONTENTS
- --------
- Your archive should have the following files:
-
- fortify.c
- fortify.doc
- fortify.h
- test.c
- ufortify.h
- ufortify.hpp
- zfortify.cpp
- zfortify.hpp
- ztest.cpp
-
-
-
-
- OVERVIEW
- --------
- Fortify is a descendant of a library I wrote way back in 1990 called
- SafeMem. It is a (fortified) shell for memory allocations. It works with
- the malloc/free family of functions, as well as new/delete in C++. It can
- be adapted to most memory management functions; the original version of
- SafeMem worked only with the Amiga's AllocMem/FreeMem. I haven't been
- writing much Amiga specific software lately, so the Amiga version of
- Fortify is way out of date, hence it's absense from this archive.
-
- Fortify is designed to detect bad things happening, or at the very
- least encourage intermittent problems to occur all the time. It is capable
- of detecting memory leaks, writes beyond and before memory blocks, and
- breaks software that relies on the state of uninitialized memory, and
- software that uses memory after it's been freed.
-
- It works by allocating extra space on each block. This includes a
- private header (which is used to keep track of Fortify's list of allocated
- memory), and two "fortification" zones or sentinals (which are used to
- detect writing outside the bound of the user's memory).
-
-
-
-
- Fortify VERSUS ZFortify
- -----------------------
- Fortify provides fortification for malloc/realloc/free, and ZFortify
- provides fortifications for new/delete. It is possible to use both
- versions in the one program, if you are mixing C and C++. Just make sure
- (as you already should be) that you never free() memory you new'd, and
- other such illegal mismatching.
- (Why _Z_ fortify? It's a long story...)
-
-
-
- Fortify INSTALLATION (C)
- ------------------------
- To use Fortify, each source file will need to #include "fortify.h". To
- enable Fortify, define the symbol FORTIFY. If FORTIFY is not defined, it
- will compile away to nothing. If you do not have stdout available, you may
- wish to set an alternate output function. See Fortify_SetOutputFunc(),
- below.
- You will also need to link in fortify.o
-
-
-
-
- ZFortify INSTALLATION (C++)
- ---------------------------
- The minimum you need to do for ZFortify is to define the symbol
- ZFORTIFY, and link zfortify.o. Each source file should also include
- "zfortify.hpp", but this isn't strictly necessary. If a file doesn't
- #include "Fortify.hpp", then it's allocations will still be fortified,
- however you will not have any source-code details in any of the output
- messages (this will be the case for all libraries, etc, unless you have the
- source for the library and can recompile it with Fortify).
- If you do not have stdout available, you may wish to set an alternate
- output function, or turn on the AUTOMATIC_LOGFILE. See
- ZFortify_SetOutputFunc() and AUTOMATIC_LOGFILE, below.
-
-
-
-
- COMPILE TIME CUSTOMIZATIONS
- ---------------------------
- The files "ufortify.h" and "ufortify.hpp" contain a number of #defines
- that you can use to customize Fortify's behavior.
-
-
- #define ZFORTIFY_PROVIDE_ARRAY_NEW
-
- Some C++ compilers have a separate operator for newing arrays. If your
- compiler does, you will need to define this symbol. If you are unsure,
- dont worry about it too much, your program won't compile or link without
- the correct setting. GCC 2.6.3 and Borland C++ 4.5 both need this symbol.
- Microsoft C++ 1.5 and SAS 6.5 C++ both dont.
-
-
- #define FORTIFY_STORAGE
-
- #define ZFORTIFY_STORAGE
-
- You can use this to apply a storage type to all of Fortify's exportable
- functions. If you are putting Fortify in an export library for example,
- you may need to put __export here, or some such rubbish.
-
-
- #define FORTIFY_BEFORE_SIZE 32
- #define FORTIFY_BEFORE_VALUE 0xA3
-
- #define FORTIFY_AFTER_SIZE 32
- #define FORTIFY_AFTER_VALUE 0xA5
-
- #define ZFORTIFY_BEFORE_SIZE 32
- #define ZFORTIFY_BEFORE_VALUE 0xA3
-
- #define ZFORTIFY_AFTER_SIZE 32
- #define ZFORTIFY_AFTER_VALUE 0xA5
-
- These values define how much "fortification" is placed around each
- memory block you allocate. Fortify will place _BEFORE_SIZE bytes worth of
- memory right before your allocation block, and _AFTER_SIZE bytes worth
- after your allocation block, and these will be initialized to _BEFORE_VALUE
- and _AFTER_VALUE respectively. If your program then accidentally writes
- too far beyond the end of the block, for example, Fortify will be able to
- detect this (so long as you didn't happen to write in _AFTER_VALUE!).
-
- If you don't want these fortifications to be allocated, specify a
- _SIZE of 0. Note that the _VALUE parameters are 8 bits.
-
-
-
- #define FILL_ON_MALLOC
- #define FILL_ON_MALLOC_VALUE 0xA7
-
- #define FILL_ON_NEW
- #define FILL_ON_NEW_VALUE 0xA7
-
- Programs often rely on uninitialized memory being certain values
- (usually 0). If you define FILL_ON_NEW, all memory that you new will be
- initialized to FILL_ON_NEW_VALUE, which you should define to be some horrid
- value (definately NOT 0). This will encourage all code that relies on
- uninitialized memory to behave rather differently when Fortify is running.
-
-
-
- #define FILL_ON_FREE
- #define FILL_ON_FREE_VALUE 0xA9
-
- #define FILL_ON_DELETE
- #define FILL_ON_DELETE_VALUE 0xA9
-
- Programmers often try to use memory after they've freed it, which can
- sometimes work (so long as noboby else has modified the memory before you
- look at it), but is incredibly dangerous and definately bad practice. If
- FILL_ON_DELETE is defined, all memory you free will be stomped out with
- FILL_ON_DELETE_VALUE, which ensures that any attempt to read freed memory
- will give incorrect results.
-
-
-
- #define CHECK_ALL_MEMORY_ON_MALLOC
- #define CHECK_ALL_MEMORY_ON_FREE
-
- #define CHECK_ALL_MEMORY_ON_NEW
- #define CHECK_ALL_MEMORY_ON_DELETE
-
- CHECK_ALL_MEMORY_ON... means that for every single memory allocation
- or deallocation, every single block of memory will be checked. This can
- considerably slow down programs if you have a large number of blocks
- allocated. You would normally only need to turn this on if you are trying
- to pinpoint where a corruption was occurring.
- A block of memory is always checked when it is freed, so if
- CHECK_ALL... isn't turned on, corruptions will still be detected
- eventually.
- You can also force Fortify to check all memory with a call to
- Fortify_CheckAllMemory(). If you have a memory corruption you can't find,
- sprinkling these through the suspect code will help narrow it down.
-
-
-
- #define PARANOID_FREE
-
- #define PARANOID_DELETE
-
- PARANOID_... - This means that zFortify traverses the memory list to
- ensure the memory you are about to free was really allocated by it. You
- probably only need this in extreme circumstances. Not having this defined
- will still trap attempts to free memory that wasn't allocated, unless
- someone is deliberately trying to fool zFortify.
- Paranoid mode adds considerable overhead to freeing memory, especially
- if you are freeing things in the same order you allocated them (Paranoid
- mode is most efficient if you are freeing things in the reverse order they
- were allocated).
-
-
-
- #define WARN_ON_MALLOC_FAIL
- #define WARN_ON_ZERO_MALLOC
- #define WARN_ON_FALSE_FAIL
- #define WARN_ON_UNSIGNED_LONG_OVERFLOW
-
- #define WARN_ON_NEW_FAIL
- #define WARN_ON_ZERO_NEW
-
- These defines enable the output of warning that aren't strictly errors,
- but can be useful to determine what lead to a program crashing.
- WARN_ON_NEW_FAIL causes a debug to be issued whenever new fails.
- WARN_ON_ZERO_NEW causes a debug to be issued whenever a new of a zero
- byte object is attempted. This is fairly unlikely in C++, and is much more
- likely when using malloc().
- WARN_ON_FALSE_FAIL causes a debug to be issued when a new is "false"
- failed. ZSee Fortify_SetNewFailRate() for more information.
- WARN_ON_UNSIGNED_LONG_OVERFLOW causes Fortify to check for breaking the
- 32 bit limit. This was more of a problem in 16-bit applications where
- breaking the 16 bit limit was much more likely. The problem is that
- Fortify adds a small amount of overhead to a memory block; so in a 16-bit
- size_t environment, if you tried to allocate 64K, Fortify would make that
- block bigger than 64K and your allocation would fail due to the presence of
- Fortify. With size_t being 32 bits for all environments worth programming
- in, this problem is extremely unlikely (Unless you plan to allocate 4
- gigabytes).
-
-
- #define AUTOMATIC_LOG_FILE
- #define LOG_FILENAME "fortify.log"
- #define FIRST_ERROR_FUNCTION
-
- If AUTOMATIC_LOG_FILE is defined (C++ version /ZFortify only), then
- Fortify will be automatically started for you, Fortify messages sent to the
- named log file, and a list of unfreed memory dumped on termination (where
- the log file will be automatically closed for you. If no Fortify was
- output, the log file will not be altered. There are timestamps in the log
- file to ensure you're reading the correct messages.
- FIRST_ERROR_FUNCTION will be called upon generation of the first
- Fortify message, so that the user can tell a Fortify report has been
- generated. Otherwise, Fortify would quietly write all this useful stuff
- out to the log file, and no-one would know to look there!
-
-
-
- #define FORTIFY_LOCK()
- #define FORTIFY_UNLOCK()
-
- #define ZFORTIFY_LOCK()
- #define ZFORTIFY_UNLOCK()
-
- In a multi-threaded environment, we need to arbitrate access to the
- foritfy memory list. This is what ZFORTIFY_LOCK() and ZFORTIFY_UNLOCK()
- are used for. The calls to ZFORTIFY_LOCK() and ZFORTIFY_UNLOCK() must
- nest. If no two threads/tasks/processes will be using the same Fortify at
- the same time, then ZFORTIFY_LOCK() and ZFORTIFY_UNLOCK() can safely be
- #defined away to nothing.
-
-
-
-
- RUN TIME CONTROL
- ----------------
- Fortify can also be controlled at run time with a few special
- functions, which compile away to nothing if FORTIFY isn't defined, and do
- nothing if Fortify has been disabled with Fortify_Disable(). These
- functions all apply to ZFortify as well (The ZFortify versions have a
- ZFortify_ prefix, of course).
-
- Fortify_Disable() - This function provides a mechanism to disable
- Fortify without recompiling all the sourcecode. It can only be called,
- though, when there is no memory on the Fortify list. (Ideally, at the
- start of the program before any memory has been allocated). If you call
- this function when there IS memory on the Fortify list, it will issue an
- error, and Fortify will not be disabled.
-
- Fortify_SetOutputFunc(Fortify_OutputFuncPtr Output) - Sets the function
- used to output all error and diagnostic messages by Fortify. The output
- function takes a single (const char *) argument, and must be able to handle
- newlines. The function returns the old pointer.
- The default output func is a printf() to stdout. Unless you are using
- AUTOMATIC_LOG_FILE, where the default is to output to the log file.
-
- Fortify_SetNewFailRate(int Percent) - Fortify will make a new attempt
- "fail" this "Percent" of the time, even if the memory IS available. Useful
- to "stress-test" an application. Returns the old value. The fail rate
- defaults to 0.
-
-
-
- DIAGNOSTIC FUNCTIONS
- --------------------
- Fortify also provides some additional diagnostic functions which can be
- used to track down memory corruption and memory leaks. If Fortify is
- disabled, these functions do nothing. If calling these functions directly
- from a debugger, remember to add the "char *file" and "unsigned long line"
- paramters to each of the calls. (The ZFortify versions have a
- ZFortify_ prefix, of course).
-
- Fortify_CheckPointer(void *uptr) - Returns true if the uptr points to a
- valid piece of Fortify'd memory. The memory must be on Fortify's list, and
- it's sentinals must be in tact. If anything is wrong, an error message is
- issued (Note - if Fortify is disabled, this function always returns true).
-
- Fortify_CheckAllMemory() - Checks the sentinals of all malloc'd memory.
- Returns the number of blocks that failed. (If Fortify is disabled, this
- function always returns 0).
-
- Fortify_OutputAllMemory() - Outputs the entire list of currently
- allocated memory. For each block is output it's Address, Size, the
- SourceFile and Line that allocated it, and the fortify scope from within it
- was allocated. If there is no memory on the list, this function outputs
- nothing. It returns the number of blocks on the list, unless Fortify has
- been disabled, in which case it always returns 0.
-
- Fortify_DumpAllMemory(scope) - Just like Fortify_OutputAllMemory,
- except all memory inside the given scope is output, and a hex dump of each
- block is included in the output.
-
- Fortify_EnterScope() - enters a level of fortify scope. Returns the
- new scope level.
-
- Fortify_LeaveScope() - leaves a level of fortify scope, it also prints
- a dump of all memory allocated within the scope being left. This can be
- very useful in tracking down memory leaks in a part of a program. If you
- place a EnterScope/LeaveScope pair around a set of functions that should
- have no memory allocated when it's done, Fortify will let you know if this
- isn't the case.
-
-
- PROBLEMS WITH THE new AND delete MACROS
- ---------------------------------------
- Due to limitations of the preprocessor, getting caller sourcecode
- information isn't as easy as it is for malloc() and free(). The macro for
- "new" which adds this information onto the new call causes syntax errors if
- you try to declare a custom new operator. The actual Fortifying works
- fine, it's just the macro expansion which causes problems.
- If this happens, you will need to place #undef's and #define's around
- the offending code (sorry). Alternatively, you can not #include
- "zfortify.hpp" for the offending file. But remember that none of the
- allocation done in that file will have sourcecode information.
-
- eg.
- #undef new
- void *X::operator new(size_t) { return malloc(size_t); }
- #define new Fortify_New
-
-
- Due to a limitation with delete, Fortify has limited information about
- where delete is being called called from, and so the the line and source
- information will often say "delete.0". If a delete is occuring from within
- another delete, Fortify will always endeavour to report the highest level
- delete as the caller.
-
- It should be possible to replace the "new.0" and "delete.0" with the
- return address of the function, which would be useful when in a debugger,
- but this would be highly architecture dependant, so I leave that as an
- exercise for the students :-).
-
-
-
- WHEN TO USE FORTIFY
- -------------------
- The simple answer to this is "All The Time". You should never be
- without Fortify when you're actually developing software. It will detect
- your bugs _as_you_write_them_, which makes them a lot easier to find. One
- programmer who recently started using Fortify when he had a very strange
- memory problem, spent at least 3 or 4 days tracking down _other_ memory
- corruption bugs that he wasn't even aware of before the program would stay
- up long enough to get to his original problem. If he'd been using Fortify
- from the beginning, this wouldn't have been a problem.
- Leave fortify enabled until the final test and release of your
- software. You probably won't want some of the slower options, such as
- CHECK_ALL_MEMORY_ON_FREE, and PARANOID_FREE. With the exception of those
- options, Fortify doesn't have a great deal of overhead. If posing a great
- problem, this overhead can be greatly reduced by cutting down on the size
- of the fortifications, and turning off the pre/post fills, but each thing
- you turn off gives fortify less information to work with in tracking your
- bugs.
-
-